package cn.calm.xhtml.util;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

import javax.servlet.ServletContext;

public abstract class ResourceLoader {

	public static List<Resource> fnidClassByPackage(String basePackage) {
		// List<String> result = new ArrayList<>();
		List<Resource> loadResources = null;
		try {
			loadResources = loadResources(basePackage);
			for (Resource resource : loadResources) {
				String fileName = resource.getFileName();
				if (fileName.endsWith(".class")) {
					String replaceAll = fileName.replaceAll("/", ".")
							.substring(0, fileName.length() - 6);
					if (replaceAll.startsWith(".")) {
						replaceAll = replaceAll.substring(1);
					}
					String trim = replaceAll.trim();
					resource.setFileName(trim);
				}
			}
		} catch (NoSuchFieldException | SecurityException
				| IllegalArgumentException | IllegalAccessException e) {
			System.out.println(e.getMessage());
		}

		return loadResources;
	}

	public static List<Resource> loadResources(String basePackage)
			throws NoSuchFieldException, SecurityException,
			IllegalArgumentException, IllegalAccessException {
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		Class<? extends ClassLoader> clazz = loader.getClass();
		Field field = clazz.getDeclaredField("repositoryURLs");
		field.setAccessible(true);
		URL[] repositories = (URL[]) field.get(loader);
		String replaceAll = basePackage.replaceAll("\\.", "/");
		List<Resource> re = new ArrayList<>();
		for (URL u : repositories) {
			if (isJar(u)) {
				re.addAll(getJarFileClass(u));
			} else {
				re.addAll(getFileSystemClass(u, replaceAll));
			}
		}
		return re;
	}

	public static List<Resource> getFileSystemClass(URL u, String replaceAll) {
		List<Resource> result = new ArrayList<>();
		try {
			File file = new File(u.toURI());

			if (file.isDirectory()) {
				for (File f : file.listFiles()) {
					result.addAll(getFileSystemClass(f.toURI().toURL(),
							replaceAll + File.separator + f.getName()));
				}
			} else if (file.getName().endsWith(".jar")) {
				result.addAll(getJarFileClass(file, ""));
			} else {
				String fileName = replaceAll;
				fileName = fileName.replaceAll("\\\\", "/");
				Resource resource = new Resource();
				resource.setUrl(u);
				resource.setFileName(fileName);
				result.add(resource);
			}
		} catch (URISyntaxException | IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	public static List<URL> loadFileURL(ServletContext sc, String path)
			throws MalformedURLException {

		String realPath = sc.getRealPath("/WEB-INF/lib");
		File file = new File(realPath);
		List<URL> classes = new ArrayList<URL>();
		File[] listFiles = file.listFiles(new FileFilter() {

			public boolean accept(File pathname) {
				return pathname.getName().endsWith("jar");
			}
		});
		String tempPath;
		if (path.startsWith("/")) {
			tempPath = path.substring(1);
		} else {
			tempPath = path;
		}
		URL url;
		for (File f : listFiles) {
			JarFile jar = null;
			try {
				jar = new JarFile(f);
				ZipEntry entry = jar.getEntry(tempPath);
				if (entry == null) {
					continue;
				}
				String temp = "";
				if (path.startsWith("/")) {
					temp = path;
				} else {
					temp = "/" + path;
				}

				url = new URL("jar:" + f.toURI() + "!" + temp);
				classes.add(url);
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				if (jar != null) {
					try {
						jar.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
		realPath = sc.getRealPath("/WEB-INF/classes");
		file = new File(realPath + "/" + path);
		if (file.isFile()) {
			classes.add(file.toURI().toURL());
		}
		realPath = sc.getRealPath("/");
		file = new File(realPath + "/" + path);
		if (file.isFile()) {
			classes.add(file.toURI().toURL());
		}
		return classes;
	}

	public static boolean isJar(URL u) {
		String protocol = u.getProtocol();
		if ("jar".equals(protocol)) {
			return true;
		} else {
			return false;
		}
	}

	public static List<Resource> getJarFileClass(URL u) {
		List<Resource> result = new ArrayList<>();
		try {
			URLConnection connection = u.openConnection();
			if (connection instanceof JarURLConnection) {
				JarURLConnection con = (JarURLConnection) connection;
				JarEntry jarEntry = con.getJarEntry();
				JarFile jarFile = con.getJarFile();
				String rootEntryPath = (jarEntry != null ? jarEntry.getName()
						: "");
				for (Enumeration<JarEntry> entries = jarFile.entries(); entries
						.hasMoreElements();) {
					JarEntry entry = entries.nextElement();
					if (entry.isDirectory()) {
						continue;
					}

					String entryPath = entry.getName();
					if (entryPath.startsWith(rootEntryPath)) {
						Resource resource = new Resource();
						resource.setFileName(entryPath);
						String real = entryPath.substring(rootEntryPath
								.length() + 1);

						URL url = new URL(u, real);
						resource.setUrl(url);
						result.add(resource);
					}
				}
				return result;

			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}

	public static List<Resource> getJarFileClass(File file, String rootEntryPath)
			throws IOException, URISyntaxException {
		List<Resource> result = new ArrayList<>();
		JarFile jarFile = null;
		try {
			jarFile = new JarFile(file);
			for (Enumeration<JarEntry> entries = jarFile.entries(); entries
					.hasMoreElements();) {
				JarEntry entry = entries.nextElement();
				if (entry.isDirectory()) {
					continue;
				}

				String entryPath = entry.getName();
				if (entryPath.startsWith(rootEntryPath)) {
					Resource resource = new Resource();
					resource.setFileName(entryPath);

					URL url = new URI("jar:" + file.toURI().toURL().toString()
							+ "!/" + entryPath).toURL();
					resource.setUrl(url);
					result.add(resource);
				}
			}
			return result;
		} finally {
			if (jarFile != null) {
				jarFile.close();
			}
		}
	}
}
